/**
 *******************************************************************************
 * @file  modem.c
 * @brief This file provides firmware functions to manage the Modem.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2019-06-26       Yangjp          First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2016, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software is owned and published by:
 * Huada Semiconductor Co., Ltd. ("HDSC").
 *
 * BY DOWNLOADING, INSTALLING OR USING THIS SOFTWARE, YOU AGREE TO BE BOUND
 * BY ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT.
 *
 * This software contains source code for use with HDSC
 * components. This software is licensed by HDSC to be adapted only
 * for use in systems utilizing HDSC components. HDSC shall not be
 * responsible for misuse or illegal use of this software for devices not
 * supported herein. HDSC is providing this software "AS IS" and will
 * not be responsible for issues arising from incorrect user implementation
 * of the software.
 *
 * Disclaimer:
 * HDSC MAKES NO WARRANTY, EXPRESS OR IMPLIED, ARISING BY LAW OR OTHERWISE,
 * REGARDING THE SOFTWARE (INCLUDING ANY ACCOMPANYING WRITTEN MATERIALS),
 * ITS PERFORMANCE OR SUITABILITY FOR YOUR INTENDED USE, INCLUDING,
 * WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, THE IMPLIED
 * WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE OR USE, AND THE IMPLIED
 * WARRANTY OF NONINFRINGEMENT.
 * HDSC SHALL HAVE NO LIABILITY (WHETHER IN CONTRACT, WARRANTY, TORT,
 * NEGLIGENCE OR OTHERWISE) FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT
 * LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION,
 * LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING FROM USE OR
 * INABILITY TO USE THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS OF DATA,
 * SAVINGS OR PROFITS,
 * EVEN IF Disclaimer HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * YOU ASSUME ALL RESPONSIBILITIES FOR SELECTION OF THE SOFTWARE TO ACHIEVE YOUR
 * INTENDED RESULTS, AND FOR THE INSTALLATION OF, USE OF, AND RESULTS OBTAINED
 * FROM, THE SOFTWARE.
 *
 * This software may be replicated in part or whole for the licensed use,
 * with the restriction that this Disclaimer and Copyright notice must be
 * included with each copy of this software, whether used in part or whole,
 * at all times.
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "modem.h"
#include "basic.h"
#include "iap.h"
#include "flash.h"
#include "com.h"

/**
 * @addtogroup HC32_Boot
 * @{
 */

/**
 * @defgroup Modem Modem
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/
__ALIGN_BEGIN static uint8_t u8FrameData[FRAME_SHELL_SIZE + PACKET_MAX_SIZE];

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup Modem_Global_Functions Modem Global Functions
 * @{
 */

/**
 * @brief  Calculate how many sectors are included in a given size.
 * @param  [in] u32Size                 Data size
 * @retval uint32_t                     Sector number
 */
static uint32_t FLASH_PageNumber(uint32_t u32Size)
{
    uint32_t u32PageNum;

    u32PageNum = u32Size / FLASH_SECTOR_SIZE;
    if ((u32Size % FLASH_SECTOR_SIZE) != 0UL)
    {
        u32PageNum += 1U;
    }

    return u32PageNum;
}

/**
 * @brief  Modem receive frame
 * @param  [out] u8RxBuff               Pointer to the buffer which the data to be received.
 * @param  [out] u16RxLength            Packet size.
 * @param  [in]  u32Timeout             Receive timeout(ms).
 * @retval An en_result_t enumeration value:
 *           - Ok: Receive finished
 *           - Error: Receive error
 */
static en_result_t Modem_RecvFrame(uint8_t *u8RxBuff, uint16_t *u16RxLength, uint32_t u32Timeout)
{
    uint16_t u16Head, u16Crc16;
    uint16_t u16PacketSize;

    *u16RxLength = 0U;
    if (Ok == COM_RecvData((uint8_t *)&u8RxBuff[FRAME_HEAD_INDEX], 2U, u32Timeout))
    {
        u16Head = u8RxBuff[FRAME_HEAD_INDEX] + (uint16_t)((uint16_t)u8RxBuff[FRAME_HEAD_INDEX + 1U] << 8U);
        if (((uint16_t)FRAME_HEAD == u16Head) &&
            (Ok == COM_RecvData((uint8_t *)&u8RxBuff[FRAME_NUM_INDEX], 2U, u32Timeout)))
        {
            if ((u8RxBuff[FRAME_NUM_INDEX] == (u8RxBuff[FRAME_XORNUM_INDEX] ^ FRAME_NUM_XOR_BYTE)) &&
                (Ok == COM_RecvData(&u8RxBuff[FRAME_LENGTH_INDEX], 2U, u32Timeout)))
            {
                u16PacketSize = u8RxBuff[FRAME_LENGTH_INDEX] + (uint16_t)((uint16_t)u8RxBuff[FRAME_LENGTH_INDEX + 1U] << 8U);
                if (((u16PacketSize >= PACKET_MIN_SIZE) && (u16PacketSize <= PACKET_MAX_SIZE)) &&
                    (Ok == COM_RecvData(&u8RxBuff[FRAME_PACKET_INDEX], u16PacketSize + 2U, u32Timeout)))
                {
                    u16Crc16 = u8RxBuff[FRAME_PACKET_INDEX + u16PacketSize] +
                               (uint16_t)((uint16_t)u8RxBuff[FRAME_PACKET_INDEX + u16PacketSize + 1U] << 8U);
                    if (Calculate_CRC16(&u8RxBuff[FRAME_PACKET_INDEX], (uint32_t)u16PacketSize) == u16Crc16)
                    {
                        *u16RxLength = u16PacketSize;
                        return Ok;
                    }
                }
            }
        }
    }

    return Error;
}

/**
 * @brief  Modem send frame
 * @param  [in] u8TxBuff                Pointer to the buffer which the data to be sent.
 * @param  [in] u16TxLength             Buffer length.
 * @retval None
 */
static void Modem_SendFrame(uint8_t *u8TxBuff, uint16_t u16TxLength)
{
    uint16_t u16Crc16;

    u8TxBuff[FRAME_LENGTH_INDEX]      = (uint8_t)(u16TxLength & 0x00FFU);
    u8TxBuff[FRAME_LENGTH_INDEX + 1U] = (uint8_t)(u16TxLength >> 8U);
    u16Crc16 = Calculate_CRC16(&u8TxBuff[FRAME_PACKET_INDEX], (uint32_t)u16TxLength);
    u8TxBuff[FRAME_PACKET_INDEX + u16TxLength]      = (uint8_t)(u16Crc16 & 0x00FFU);
    u8TxBuff[FRAME_PACKET_INDEX + u16TxLength + 1U] = (uint8_t)(u16Crc16 >> 8U);
    COM_SendData(&u8TxBuff[0U], FRAME_PACKET_INDEX + u16TxLength + 2U);
}

/**
 * @brief  Modem process
 * @param  [in] u32Timeout              Communication timeout(ms)
 * @retval An en_result_t enumeration value:
 *           - Ok: Communication done
 *           - ErrorTimeout: Communication timeout
 */
en_result_t Modem_Process(uint32_t u32Timeout)
{
    en_result_t enRet;
    uint8_t u8AddrValidFlag;
    uint32_t u32TimeoutCnt = 0U;
    uint16_t u16PacketSize, u16DataSize, u16TxLength;
    uint32_t u32FlashAddr = 0UL, u32AppAddr = 0U;
    uint32_t u32PageNum, u32Cnt;
    uint16_t u16FlashCrc;
    uint32_t u32Temp;

    for (;;)
    {
        if (Ok == Modem_RecvFrame(&u8FrameData[0], &u16PacketSize, FRAME_RECV_TIMEOUT))
        {
            u32TimeoutCnt = 0U;
            u8AddrValidFlag = 0U;
            u16TxLength = 0U;

            u16DataSize = u16PacketSize - PACKET_INSTRUCT_SIZE;
            if ((uint8_t)PACKET_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX])
            {
                u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] + ((uint32_t)u8FrameData[PACKET_ADDRESS_INDEX + 1U] << 8U) +
                               ((uint32_t)u8FrameData[PACKET_ADDRESS_INDEX + 2U] << 16U) + ((uint32_t)u8FrameData[PACKET_ADDRESS_INDEX + 3U] << 24U);
                if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE)))
                {
                    u8AddrValidFlag = 1U;
                    u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_OK;
                }
                else
                {
                    u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ADDR_ERROR;
                }
            }
            else
            {
                u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_OK;
            }

            switch (u8FrameData[PACKET_CMD_INDEX])
            {
                case PACKET_CMD_HANDSHAKE:
                    /* Reserved */
                    break;
                case PACKET_CMD_JUMP_TO_APP:
                    u32Temp = APP_FLAG;
                    (void)EFM_WriteFlash(APP_FLAG_ADDRESS, (uint8_t *)&u32Temp, 4UL);
                    (void)EFM_WriteFlash(APP_RUN_ADDRESS, (uint8_t *)&u32AppAddr, 4UL);
                    Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SIZE);
                    return Ok;
                case PACKET_CMD_APP_DOWNLOAD:
                    if (1U == u8AddrValidFlag)
                    {
                        enRet = EFM_WriteFlash(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], (uint32_t)u16DataSize);
                        if (Ok != enRet)
                        {
                            u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ERROR;
                        }
                    }
                    break;
                case PACKET_CMD_APP_UPLOAD:
                    if (1U == u8AddrValidFlag)
                    {
                        u32Temp = u8FrameData[PACKET_DATA_INDEX] + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 1U] << 8U) +
                                  ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 2U] << 16U) + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 3U] << 24U);
                        if (u32Temp > PACKET_DATA_SIZE)
                        {
                            u32Temp = PACKET_DATA_SIZE;
                        }
                        enRet = EFM_ReadFlash(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp);
                        if (Ok != enRet)
                        {
                            u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ERROR;
                        }
                        else
                        {
                            u16TxLength = (uint16_t)u32Temp;
                        }
                    }
                    break;
                case PACKET_CMD_ERASE_FLASH:
                    if (1U == u8AddrValidFlag)
                    {
                        // Check Vector Table Relocation
                        if ((u32FlashAddr % ((VECTOR_TABLE_STEP > FLASH_SECTOR_SIZE) ? VECTOR_TABLE_STEP : FLASH_SECTOR_SIZE)) != 0UL)
                        {
                            u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ADDR_ERROR;
                        }
                        else
                        {
                            u32Temp = u8FrameData[PACKET_DATA_INDEX] + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 1U] << 8U) +
                                      ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 2U] << 16U) + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 3U] << 24U);
                            if ((u32Temp + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE))
                            {
                                u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ERROR;
                            }
                            else
                            {
                                /* Erase flash for APP flag sector */
                                (void)EFM_EraseSector(APP_FLAG_ADDRESS);
                                u32AppAddr = u32FlashAddr;
                                u32PageNum = FLASH_PageNumber(u32Temp);
                                for (u32Cnt = 0U; u32Cnt < u32PageNum; u32Cnt++)
                                {
                                    enRet = EFM_EraseSector(u32FlashAddr + (u32Cnt * FLASH_SECTOR_SIZE));
                                    if (Ok != enRet)
                                    {
                                        u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ERROR;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    break;
                case PACKET_CMD_FLASH_CRC:
                    if (1U == u8AddrValidFlag)
                    {
                        u32Temp = u8FrameData[PACKET_DATA_INDEX] + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 1U] << 8U) +
                                  ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 2U] << 16U) + ((uint32_t)u8FrameData[PACKET_DATA_INDEX + 3U] << 24U);
                        enRet = FLASH_GetCrcValue(u32FlashAddr, u32Temp, &u16FlashCrc);
                        if (Ok != enRet)
                        {
                            u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ADDR_ERROR;
                        }
                        else
                        {
                            u8FrameData[PACKET_DATA_INDEX]      = (uint8_t)(u16FlashCrc & 0x00FFU);
                            u8FrameData[PACKET_DATA_INDEX + 1U] = (uint8_t)(u16FlashCrc >> 8U);
                            u16TxLength = 2U;
                        }
                    }
                    break;
                case PACKET_CMD_APP_UPGRADE:
                    /* Reserved */
                    break;
                default:
                    u8FrameData[PACKET_RESULT_INDEX] = (uint8_t)PACKET_ACK_ERROR;
                    break;
            }
            Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SIZE + u16TxLength);
        }
        else
        {
            u32TimeoutCnt += FRAME_RECV_TIMEOUT;
        }
        /* Communication timeout */
        if (u32TimeoutCnt >= u32Timeout)
        {
            return ErrorTimeout;
        }
    }
}

/**
 * @}
 */

/**
 * @}
 */

/**
* @}
*/

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
